package ar.edu.unlu.sistemas.p2p.android;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.Inet4Address;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;

import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.database.sqlite.SQLiteConstraintException;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.os.Binder;
import android.os.Environment;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.widget.RemoteViews;
import android.widget.Toast;
import ar.edu.unlu.sistemas.p2p.android.sql.IndexOpenHelper;
import ar.edu.unlu.sistemas.p2p.business.exception.MovirdroidException;
import ar.edu.unlu.sistemas.p2p.business.exception.MovirdroidExceptionMessages;
import ar.edu.unlu.sistemas.p2p.business.module.AppModule;
import ar.edu.unlu.sistemas.p2p.business.task.Result;
import ar.edu.unlu.sistemas.p2p.business.util.Constants;
import ar.edu.unlu.sistemas.p2p.business.util.Logger;
import ar.edu.unlu.sistemas.p2p.business.util.Params;
import ar.edu.unlu.sistemas.p2p.business.vo.DocumentVO;
import ar.edu.unlu.sistemas.p2p.business.vo.PeerVO;
import ar.edu.unlu.sistemas.p2p.business.vo.PingResultVO;
import ar.edu.unlu.sistemas.p2p.business.vo.QueryResultVO;
import ar.edu.unlu.sistemas.p2p.business.vo.TermFrecuencyVO;
import ar.edu.unlu.sistemas.p2p.business.vo.UpdateIndexVO;
import ar.edu.unlu.sistemas.p2p.business.vo.WordVO;

public class AppService extends Service {

	private AppModule module;
	private final IBinder mBinder = new LocalBinder();
	protected boolean indexUpdateLaunched = false;
	private static final int NOTIFICATION_ID_UPD = 42;
	private static final int NOTIFICATION_ID_STA = 43;
	private Notification notification;
    private NotificationManager notificationManager;
	
	public void onCreate() {
		notificationManager = (NotificationManager) getApplicationContext().getSystemService(
    			getApplicationContext().NOTIFICATION_SERVICE);
		CharSequence charsec = "Movirdroid"; 
		// configure the notification
    	notification = new Notification(R.drawable.icon, charsec, System
    			.currentTimeMillis());
    	// The PendingIntent to launch our activity if the user selects this notification
        PendingIntent contentIntent = PendingIntent.getActivity(this, 0,
                new Intent(this, QueryActivity.class),PendingIntent.FLAG_ONE_SHOT);
        
        notification.setLatestEventInfo(this, charsec,
                charsec, contentIntent);
        
		this.startForeground(NOTIFICATION_ID_STA, notification);
	}
	
	public class LocalBinder extends Binder {
        AppService getService() {
            return AppService.this;
        }
    }
	
	@Override
	public int onStartCommand(Intent intent, int flags, int startId) {
		int port = intent.getIntExtra(Constants.PORT, Params.TCP_LISTEN_PORT);
		String alias = intent.getStringExtra(Constants.ALIAS);
		this.initConfig();
		this.module = AppModule.getInstanceWithParams(port, new IndexOpenHelper(this),this.getWifiState(),alias);
		//Crea el directorio en la memoria SD
		this.getDirectory();
		return START_STICKY;
	}


	@Override
	public IBinder onBind(Intent arg0) {
		return mBinder;
	}
	
	final Runnable mShowError = new Runnable() {
        public void run() {
            updateIndex();
        }
    };
	
	public void connectWith(String ipAddr, int port) {
		try {
			this.module.requestConnectionToPeer(Inet4Address.getByName(ipAddr), port);
		} catch (UnknownHostException uhe) {
			
		}
	}
	
	private void initConfig() {
		try {
			Params.loadPreferences(this.openFileInput(Constants.CONFIG_FILE_PATH));
		} catch (MovirdroidException me) {
			Logger.logError(this.getClass().getSimpleName(),me.getMessage(),me);
		} catch (FileNotFoundException fnfe) {
			try {
				Logger.logInfo(this.getClass().getSimpleName(),"No existe el archivo de config. Se crea uno");
				Params.savePreferences(this.openFileOutput(Constants.CONFIG_FILE_PATH,Context.MODE_PRIVATE));
			} catch (MovirdroidException me) {
				Logger.logError(this.getClass().getSimpleName(),me.getMessage(),me);
			} catch (FileNotFoundException fnfe2) {
				Logger.logError(this.getClass().getSimpleName(),MovirdroidExceptionMessages.ERROR_FINDING_CONFIG,fnfe2);
			}
		}
	}
	
	public String ping() {
		return this.module.doPing();
	}
	
	public Collection<PeerVO> getPeers() {
		return this.module.getAllPeers();
	}
	
	private Collection<PingResultVO> castPingResults(Collection<Result> resultsColl) {
		Iterator<Result> results = resultsColl.iterator();
		Collection<PingResultVO> coll = new ArrayList<PingResultVO>();
		while (results.hasNext()) {
			coll.add((PingResultVO)results.next());
		}
		return coll;
	}
	
	private Collection<QueryResultVO> castQueryResults(Collection<Result> resultsColl) {
		Iterator<Result> results = resultsColl.iterator();
		Collection<QueryResultVO> coll = new ArrayList<QueryResultVO>();
		while (results.hasNext()) {
			coll.add((QueryResultVO)results.next());
		}
		return coll;
	}
	
	public Collection<PingResultVO> getPingResults(String msgId) {
		return this.castPingResults(this.module.getTaskResults(msgId));
	}
	
	public Collection<PingResultVO> finishPingResults(String msgId) {
		return this.castPingResults(this.module.finishTask(msgId));
	}
	
	public Collection<QueryResultVO> getQueryResults(String msgId) {
		return this.castQueryResults(this.module.getTaskResults(msgId));
	}
	
	public Collection<QueryResultVO> finishQueryResults(String msgId) {
		return this.castQueryResults(this.module.finishTask(msgId));
	}
	
	public boolean isTaskFinished(String msgId) {
		return this.module.isTaskFinished(msgId);
	}
	
	public Collection<WordVO> findWords(String word) {
		return this.module.findWords(word);
	}
	
	public Collection<DocumentVO> findDocs(String doc) {
		return this.module.findDocs(doc);
	}
	
	public Collection<TermFrecuencyVO> findTFs(String doc, String word) {
		return this.module.findTFs(doc,word);
	}
	
	public void addWord(String word) {
		this.module.addWord(word);
	}
	
	public void addDoc(String doc) {
		this.module.addDoc(doc);
	}
	
	public void addTF(String doc, String word, float value) {
		try {
			this.module.addTF(doc,word,value);
		} catch (SQLiteConstraintException sqle) {
		
		}
	}
	
	public Collection<QueryResultVO> executeQuery(String query) {
		return this.module.processQuery(query);
	}
	
	public String doQuery(String query) {
		return this.module.doQuery(query);
	}
	
	public void addFile(String fileName) {
		if (Constants.SD_UNMOUNTED != this.getSDCardState()) {
			File file = new File(this.getDirectory(), fileName);
			if (file != null && file.exists()); {
				this.module.addFile(file, fileName);
			}
		}
	}
	
	public File getFile(String fileName) {
		File file = null;
		if (Constants.SD_UNMOUNTED != this.getSDCardState()) {
			file = new File(this.getDirectory(), fileName);
		}
		return file;
	}
	
	public void removeDoc(String docName) {
		this.module.removeDoc(docName);
	}
	
	public void createFile(String fileName, String text) {
		if (Constants.SD_MOUNTED_WRITE == this.getSDCardState()) {
			File file = new File(this.getDirectory(), fileName);
			if (file != null && !file.exists()) {
				try {
					OutputStream os = new FileOutputStream(file);
					os.write(text.getBytes());
					os.close();
				} catch (IOException io) {
					//throw new MovirdroidException("Ocurrio un error I/O");
				}
			}
			
		} else {
			//throw new MovirdroidException("La tarjeta SD no esta montada");
		}
	}
	
	public String[] listAllFiles() {
		if (Constants.SD_MOUNTED_WRITE == this.getSDCardState()) {
			File dir = this.getDirectory();
			return dir.list();
		} else {
			return null;
			//throw new MovirdroidException("La tarjeta SD no esta montada");
		}
		
	}
	
	public Collection<UpdateIndexVO> listNotIndexedFiles() {
		if (Constants.SD_MOUNTED_WRITE == this.getSDCardState()) {
			File dir = this.getDirectory();
			return this.module.getNotIndexedFiles(dir.list());
		} else {
			return null;
			//throw new MovirdroidException("La tarjeta SD no esta montada");
		}
		
	}
	
	public void connectToPeerWithAddress(String dstAddress, int dstPort) {
		try {
			this.module.requestConnectionToPeer(Inet4Address.getByName(dstAddress), dstPort);
		} catch (UnknownHostException uhe) {
			//throw new MovirdroidException("Host desconocido");
		}
	}
	
	private int getSDCardState() {

		String state = Environment.getExternalStorageState();

		if (Environment.MEDIA_MOUNTED.equals(state)) {
			// We can read and write the media
			return Constants.SD_MOUNTED_WRITE;
		} else if (Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
			// We can only read the media
			return Constants.SD_MOUNTED_READ;
		} else {
			// Something else is wrong. It may be one of many other states, but all we need
			//  to know is we can neither read nor write
			return Constants.SD_UNMOUNTED;
		}
	}
	
	private File getDirectory() {
		File rootDir = Environment.getExternalStoragePublicDirectory(Params.CORPUS_DIR);
		if (!rootDir.exists()) {
			rootDir.mkdirs();
		}
		return rootDir;
	}
	
	public final Handler mHandler = new Handler() {
		//Se envían mensajes al handler que realizarán una acción
		//determinada por este método debido a que otro thread que no
		//sea el creador de la UI puede modificarla
		//Este código se ejecuta en el main thread
		public void handleMessage(Message msg) {
			super.handleMessage(msg);
			switch(msg.what) {
				case 0:
					this.post(mShowToast);
					break;	
				default:
					break;
			}
		}
	};
	
	final Runnable mShowToast = new Runnable() {
        public void run() {
        	showToast();
        }
    };
    
    public void showToast() {
    	Toast toast = Toast.makeText(this,"Indice actualizado", Toast.LENGTH_LONG);
    	toast.show();
    }
    
    public void createUpadteIndexProgressNotification() {
    	 // get the layout
        //setContentView(R.layout.updateindex_progressbar);

        // configure the intent
        Intent intent = new Intent(this, AppService.class);
        final PendingIntent pendingIntent = PendingIntent.getActivity(getApplicationContext(), 0, intent, 0);
    	
    	// configure the notification
    	notification = new Notification(R.drawable.icon, "actualizando el indice", System
    			.currentTimeMillis());
    	notification.flags = notification.flags | Notification.FLAG_ONGOING_EVENT | Notification.FLAG_NO_CLEAR;
    	notification.contentView = new RemoteViews(getApplicationContext().getPackageName(), R.layout.updateindex_progressbar);
    	notification.contentIntent = pendingIntent;
    	notification.contentView.setImageViewResource(R.id.updateindex_status_icon, R.drawable.update_index);
    	notification.contentView.setTextViewText(R.id.updateindex_status_text, "actualizando el indice");
    	notification.contentView.setProgressBar(R.id.updateindex_status_progress, 100, 0, false);
    	notificationManager.notify(NOTIFICATION_ID_UPD, notification);
    }
    
    public void updateIndexAsinc() {
    	this.module.indexUpdateLaunched = true;
    	final Thread thread = new Thread() {
    		public void run() {
    			updateIndex();
    		}
    	};
    	thread.start();
    }
    
    private void updateIndex() {
    	Collection<UpdateIndexVO> files = this.listNotIndexedFiles();
		if (files != null && (files.size()>0)) {
			this.createUpadteIndexProgressNotification();
			int maxProgress = files.size();
			int i = 1;
			Iterator<UpdateIndexVO> filesIt = files.iterator();
			UpdateIndexVO updateActionVO;
			while (filesIt.hasNext() && (Constants.SD_UNMOUNTED != this.getSDCardState())) {
				updateActionVO = filesIt.next();
				if (UpdateIndexVO.ACTION_INSERT.equals(updateActionVO.getAction())) {
					this.addFile(updateActionVO.getDocName());
				} else {
					this.removeDoc(updateActionVO.getDocName());
				}
				notification.contentView.setProgressBar(R.id.updateindex_status_progress, maxProgress, i++, false);
                // inform the progress bar of updates in progress
                notificationManager.notify(NOTIFICATION_ID_UPD, notification);
			}
			notificationManager.cancel(NOTIFICATION_ID_UPD);
			this.mHandler.sendMessage(this.mHandler.obtainMessage(0));
		}
		this.module.indexUpdateLaunched = false;
	}
    
    public Map<String,String> loadPreferences() {
    	return Params.getMapPreferences();
    }
    
    public void savePreferences(Map<String,String> prefMap) {
    	try {
    		Params.setMapPreferences(prefMap);
    		Params.savePreferences(this.openFileOutput(Constants.CONFIG_FILE_PATH, Context.MODE_PRIVATE));
    		Logger.logInfo(this.getClass().getSimpleName(),"Se persistio la configuracion");	
    	} catch (MovirdroidException me) {
				Logger.logError(this.getClass().getSimpleName(),me.getMessage(),me);
			} catch (FileNotFoundException fnfe2) {
				Logger.logError(this.getClass().getSimpleName(),MovirdroidExceptionMessages.ERROR_FINDING_CONFIG,fnfe2);
			}
    }
    
    public boolean isIndexUpdated() {
    	Collection<UpdateIndexVO> list = this.listNotIndexedFiles();
    	return ((list != null) && (list.isEmpty()));
    }
    
    public boolean isConnected() {
    	return this.module.isConnected();
    }
    
    public void connect() {
		this.module.connect(this.getWifiState());	
    }
     
    private String getWifiState() {
    	WifiManager wifimanager =(WifiManager) this.getSystemService(WIFI_SERVICE);
    	String wifiMac = null;
    	if (wifimanager.isWifiEnabled() &&
				(wifimanager.getConnectionInfo() != null) &&
				(wifimanager.getConnectionInfo().getNetworkId() != -1)) {
			WifiInfo wifinfo = wifimanager.getConnectionInfo();
			Logger.logInfo(this.getClass().getName(), wifinfo.getMacAddress());
			wifiMac = wifinfo.getMacAddress();
    	} else if (Constants.IS_EMULATOR) {
			wifiMac = "010101010101";
		}
    	Logger.logInfo(AppService.class.getSimpleName(), "Mac: "+(wifiMac != null ? wifiMac : "null"));
    	return wifiMac;
    }
    
    public String getMIMEType(File f)
    {
    	String end = f.getName().substring(f.getName().lastIndexOf(".")
    			+1, f.getName().length()).toLowerCase();
    	String type = "";
    	if(end.equals("mp3") || end.equals("aac") || end.equals("aac") ||
    			end.equals("amr") || end.equals("mpeg") || end.equals("mp4")) type =
    				"audio";
    	else if(end.equals("jpg") || end.equals("gif") || end.equals
    			("png") || end.equals("jpeg")) type = "image";
    	else if(end.equals("htm") || end.equals("html") || end.equals("txt")) type = "text";

    	type += "/*";
    	return type;
    }
    
    public void onLowMemory() {
    	Params.USE_WORDS_CACHE = false;
    	this.module.cleanCaches();
    }
    
    public void onDestroy() {
    	this.module.stopListen();
    }
    
    public boolean isIndexUpdateLaunched() {
    	return this.module.indexUpdateLaunched;
    }
    
}
